package beat

import (
	"log"
	"math"
	"math/rand"

	"gitee.com/uxff/beatdemo/util"
)

type Bot struct {
	one        *One
	threatList *ThreatList

	randIdx int

	recvQ chan Dmg
	done  chan bool
}

func NewBot(one *One) (bot *Bot) {
	bot = &Bot{
		one:   one,
		done:  make(chan bool),
		recvQ: make(chan Dmg, 10),

		threatList: NewThreatList(),
	}
	bot.one.OutObserver = append(bot.one.OutObserver, func(d Dmg) {
		if d.From == bot.one.Id {
			//来自自己的，或者来自别的bot的，忽略
			return
		}
		bot.threatList.Bear(d)
		bot.recvQ <- d //one收到的消息发给bot
		//bot.recvQ 用于触发主动寻找目标 会触发hit 要顺序处理很慢 所以可能阻塞 多了可以丢弃
	})
	return
}

func (b *Bot) Start(pmap map[int32]*One) {
	//TODO 对threatList.List有并发读写问题
	go b.threatList.Serve()
	defer b.threatList.Shutdown()

	var aoeMultiple float32 = 1.0

	// selTargetTimes := 0
	for b.one.H > 0 {
		b.randIdx++
		select {
		case d := <-b.recvQ: //接受来自b.one的消息
			switch d.T {
			case DmgTypeHurt, DmgTypeThreatHeal:

				var targetId int32 = b.aim(d, pmap)
				// log.Printf("bot(%s) find %d in threat list, bot gid:%d", b.one.ToString(), targetId, util.GetGoroutineID())
				if targetId < 0 {
					targetId = d.From
				}
				needToKick := false

				if b.randIdx%7 == 0 {
					b.one.Aoe(pmap, 0.4)
				} else if b.randIdx%29 == 0 {
					b.one.Aoe(pmap, 0.8*aoeMultiple)
					aoeMultiple *= 1.25
				}
				if b.randIdx > 30 && b.randIdx%11 == 0 {
					b.one.Heal(b.one)
				}
				if ok := b.one.Hit(pmap[targetId]); !ok {
					needToKick = true
				}
				if pmap[targetId].H <= 0 {
					needToKick = true
				}
				if needToKick {
					d2 := Dmg{T: DmgTypeDie, From: targetId}
					b.threatList.Bear(d2)
					// b.threatList.kickOne(targetId) //并发问题
				}
				// if targetId < 0 {
				// 	log.Printf("has none in threat list")
				// 	time.Sleep(time.Millisecond * 200)
				// 	if selTargetTimes > 10 {
				// 		log.Printf("try too many times failed in threat list")
				// 		return
				// 	}
				// 	selTargetTimes++
				// 	continue
				// }
			case DmgTypeDie:
			}

		case <-b.done:
			return
		}
	}
	log.Printf("bot(%s) stopped hit because die", b.one.ToString())
}

func (b *Bot) aim(d Dmg, pmap map[int32]*One) (targetId int32) {
	thresholdDot := b.one.levelRate * b.one.levelRate / 27 //27 bigger is harder, more random aim
	randomRoll := util.MixRandAuto(d.P / 2000)
	if len(b.threatList.List) > 0 && randomRoll < thresholdDot {
		log.Printf("bot(%s) will aim random target roll:%v thresh:%v levelRate:%v", b.one.ToString(), randomRoll, thresholdDot, b.one.levelRate)
		return b.aimRandom(pmap)
	}
	// log.Printf("bot(%s) will aim ordered target", b.one.ToString())
	return b.aimOrdered(pmap)
}

func (b *Bot) aimOrdered(pmap map[int32]*One) (targetId int32) {
	targetId = -1
	for i := 0; i < len(b.threatList.List); i++ {
		targetId = b.threatList.GetTop(i)
		if targetId > 0 {
			if pmap[targetId].H <= 0 {
				targetId = -1
				continue
			}
			break
		}
	}
	return
}

func (b *Bot) aimRandom(pmap map[int32]*One) (targetId int32) {
	targetId = -1
	for i := 0; i < 10 && len(b.threatList.List) > 0; i++ {
		listIdx := rand.Int() % len(b.threatList.List)
		targetId = b.threatList.List[listIdx]
		if pmap[targetId].H <= 0 {
			targetId = -1
			continue
		}
	}
	return
}

func (b *Bot) Shutdown() {
	log.Printf("bot(%s) have urged to shutdown", b.one.ToString())
	if b.one.H > 0 {
		b.done <- true
	}
}

func UpBotMaxByRate(npcLvRate float32, pNum int, tint float32) float32 {
	return (BaseMaxH * npcLvRate) * float32(pNum) * float32(math.Sqrt(float64(pNum))) * npcLvRate * npcLvRate * tint //0.224 bigger is harder, more h of boss
}
